﻿using System;
using System.Collections.ObjectModel;
using System.Threading;

using Sunyi.Project.WebPoint.Common;
using Sunyi.Project.WebPoint.Configuration;
using Sunyi.Project.WebPoint.DataContract;
using Sunyi.Project.WebPoint.ServiceAccess;

namespace Sunyi.Project.WebPoint.BusinessLogic
{
    public class AuthenticationManager
    {
        public bool ValidateUser(string userName, string password)
        {
            if (string.IsNullOrEmpty(userName)) return false;

            bool flag = false;
            string authenticationMode = SystemConfiguration.Authentication[WebPointGlobalConstant.Mode];

            if (authenticationMode == AuthenticationMode.Database.ToString())
            {
                flag = DatabaseAuthenticate(userName, password);
            }
            else if (authenticationMode == AuthenticationMode.Domain.ToString())
            {
                flag = DomainAuthenticate(userName, password);
            }
            else if (authenticationMode == AuthenticationMode.Service.ToString())
            {
                flag = ServiceAuthenticate(userName, password);
            }
            else if (authenticationMode == AuthenticationMode.DomainAndDatabase.ToString())
            {
                flag = DomainAuthenticate(userName, password);

                // If domain authentication failed, do local database authentication.
                if (!flag) flag = DatabaseAuthenticate(userName, password);
            }
            else if (authenticationMode == AuthenticationMode.ServiceAndDatabase.ToString())
            {
                flag = ServiceAuthenticate(userName, password);

                // If service authentication failed, do local database authentication.
                if (!flag) flag = DatabaseAuthenticate(userName, password);
            }
            else
            {
                // Nothing to do.
                // Unknown authentioncateion mode specified, just ingore it.
            }

            return flag;
        }

        public bool ValidateToken(Guid tokenId, string accessCode)
        {
            bool flag = false;
            var tokenManager = new TokenManager();

            var token = tokenManager.Select(tokenId);
            if (token != null && (!token.IsActive || token.IsExpired)) { tokenManager.Erasure(token.Id); token = null; }

            if (token != null)
            {
                flag = tokenManager.VerifyToken(token, accessCode);
            }
            else
            {
                if (SystemConfiguration.PersonnelServiceEnabled)
                {
                    var serviceProxy = new SecurityTokenServiceProxy();
                    token = serviceProxy.GetToken(tokenId, accessCode);

                    flag = AuthenticatieToken(token);
                }
            }

            return flag;
        }

        #region Private Method

        private bool DatabaseAuthenticate(string userName, string password)
        {
            bool flag = false;

            var userManager = new UserManager();
            var user = userManager.SelectByName(userName);
            if (user != null) flag = user.IsActive && user.Password == EncrpytionHelper.EncrpytByMD5(password);

            return flag;
        }

        private bool DomainAuthenticate(string userName, string password)
        {
            bool flag = false;
            string domain = SystemConfiguration.Authentication[WebPointGlobalConstant.Domain];

            try
            {
                flag = AdHelper.ValidateDomainUser(userName, password, domain);
            }
            catch { }

            if (flag)
            {
                var userManager = new UserManager();
                var user = userManager.SelectByName(userName);

                // if user exists in local database, check the IsActive property.
                if (user != null) flag = user.IsActive;

                if (flag)
                {
                    // Update password or Save user afer authentication passed.
                    if (user == null) 
                    {
                        user = GenerateNewUser(userName, password);
                        userManager.Insert(user);
                    }
                    else 
                    {
                        user.Password = EncrpytionHelper.EncrpytByMD5(password);
                        userManager.Update(user);
                    }
                }
            }

            return flag;
        }

        private bool ServiceAuthenticate(string userName, string password)
        {
            bool flag = false;

            var securityTokenServiceProxy = new SecurityTokenServiceProxy();
            Token token = securityTokenServiceProxy.IssueToken(userName, password);

            if (token != null) flag = AuthenticatieToken(token);

            return flag;
        }

        private bool AuthenticatieToken(Token token)
        {
            bool flag = false;

            var userManager = new UserManager();
            var user = userManager.SelectByName(token.UserName);

            // if user exists in local database, check the IsActive property.
            if (user != null)
            {
                flag = user.IsActive;
            }
            else
            {
                // Pass the authentication for new user.
                flag = true;
            }

            if (flag)
            {
                // Save token into local database.
                var tokenManager = new TokenManager();
                if (tokenManager.Select(token.Id) == null) tokenManager.Insert(token);
                Thread.CurrentThread.Name = token.UserName;

                // Update or Save user afer authentication passed.
                var newUser = DownloadUserInfo(token.UserName);
                if (newUser != null)
                {
                    if (user != null)
                    {
                        if (user.Id != newUser.Id) newUser.Id = user.Id;
                        if (user.UpdateTimestamp != newUser.UpdateTimestamp) userManager.Save(newUser);
                    }
                    else
                    {
                        userManager.Insert(newUser);
                    }
                }
            }

            return flag;
        }

        private User GenerateNewUser(string userName, string password)
        {
            var user = new User();
            user.Id = Guid.NewGuid();
            user.UserId = userName;
            user.UserName = userName;
            user.Password = EncrpytionHelper.EncrpytByMD5(password);
            user.Department = SystemConfiguration.WebSite[WebPointGlobalConstant.UserDefaultDepartment];
            user.Mail = userName.Replace(WebPointGlobalConstant.Dot, string.Empty) + WebPointGlobalConstant.At + SystemConfiguration.WebSite[WebPointGlobalConstant.UserDefaultMailPostfix];
            user.IsActive = true;

            foreach (var roleId in RoleManager.DefaultRoles) if (!user.Roles.Contains(roleId)) user.Roles.Add(roleId);

            return user;
        }

        private User DownloadUserInfo(string userName)
        {
            var userServiceProxy = new UserServiceProxy();
            var user = userServiceProxy.GetUser(userName);

            if (user != null)
            {
                var roleManager = new RoleManager();
                var roles = roleManager.SelectAll();

                Collection<Guid> removeRoles = new Collection<Guid>();
                foreach (var roleId in user.Roles)
                {
                    bool flag = false;
                    foreach (var role in roles) { if (role.Id == roleId) { flag = true; break; } }

                    if (!flag) removeRoles.Add(roleId);
                }

                foreach (var roleId in removeRoles) user.Roles.Remove(roleId);
            }

            return user;
        }

        #endregion
    }
}
